package com.zw.cloud.influxdb.service.device.report.data.stenter;

import com.google.common.collect.Lists;
import com.zw.cloud.common.utils.BigDecimalUtils;
import com.zw.cloud.influxdb.entity.BaseTenterCraft;
import com.zw.cloud.influxdb.entity.DeviceVO;
import com.zw.cloud.influxdb.enums.IotDataRangeEnum;
import com.zw.cloud.influxdb.enums.StenterInfluxDBEnum;
import com.zw.cloud.influxdb.service.InfluxdbQueryService;
import com.zw.cloud.influxdb.utils.IotInfoUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;


@Service
@Slf4j
public class StenterReverseAlgorithmService {

    @Autowired
    private InfluxdbQueryService influxdbQueryService;


    public BaseTenterCraft reverseAlgorithmForStenter(String iotCode, LocalDateTime startTime, LocalDateTime endTime) {
        String sql = "SELECT * FROM device_report_data_stenter where time >= $startTime and time <= $endTime and device=$device and dataType = 'report' order by time asc";
        Map<String, Object> bindParams = new HashMap<>();
        bindParams.put("startTime", startTime);
        bindParams.put("endTime", endTime);
        bindParams.put("device",iotCode);
        List<DeviceVO> allIotInfoDtoList = influxdbQueryService.queryDeviceReportData(sql, DeviceVO.class, bindParams);
        if (CollectionUtils.isEmpty(allIotInfoDtoList)) {
            return null;
        }

        Duration duration = Duration.between(startTime, endTime);
        long totalMinutes = duration.toMinutes();
        // 总时间段取值
        /**
         * A、流转卡运行时间>=6分钟，则总时间段取“开始时间”至“结束时间-3分钟”；
         * B、流转卡运行时间<6分钟，则总时间段取“开始时间”至“结束时间“。
         */
        List<DeviceVO> caleIotInfoDtoList;
        if (totalMinutes >= 6) {
            long endEpochMilli = endTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - 1000 * 60 * 3;
            caleIotInfoDtoList = allIotInfoDtoList.stream().filter(iotInfoDto -> {
                long epochMilli = iotInfoDto.getTime().toEpochMilli();
                return epochMilli < endEpochMilli;
            }).collect(Collectors.toList());
        } else {
            caleIotInfoDtoList = allIotInfoDtoList;
        }

        // 有效时间取值
        /**
         * 生产时间<3分钟，判断生产过程中是否出现停车(车速<5)，如“是”，则视为无效生产，不计算工艺参数。
         *                                             如“否”，则取值时间为 “生产时间”；【中车开始时间 - 中车结束时间】
         * C 生产时间≥3分钟，判断生产过程中是否出现停车(车速<5)，如“是”，则取值时间为 最一次停车后1分钟时间 到 中车结束时间，取中间1/3，作为生产时间，
         * 如果生产时间<=1分钟,不计算工艺参数 结束。否则作为生产时间
         *                                            如“否”，则取值时间为生产时间中间1/3时间段，作为生产时间
         */
        long minutes = duration.toMinutes();
        // 生产时间<3分钟
        if (minutes < 3) {
            // 判断生产过程中是否出现停车(车速<10)
            Optional<DeviceVO> infoDto = caleIotInfoDtoList.stream().filter(iotInfoDto -> Objects.isNull(iotInfoDto.getSpeed()) || iotInfoDto.getSpeed().compareTo(new BigDecimal(5)) < 0).findAny();
            if (infoDto.isPresent()) {
                log.info("[StenterReverseAlgorithmService][reverseAlgorithmForStenter]minutes is {},生产时间小于3分钟并出现停车",minutes);
                caleIotInfoDtoList = Lists.newArrayList();
            }
            log.info("[StenterReverseAlgorithmService][reverseAlgorithmForStenter]minutes is {},生产时间小于3分钟没有出现停车",minutes);

        } else {
            // 生产时间>=3分钟
            Optional<DeviceVO> stopIotInfoDtoOptional = caleIotInfoDtoList.stream().filter(iotInfoDto -> Objects.isNull(iotInfoDto.getSpeed()) || iotInfoDto.getSpeed().compareTo(new BigDecimal(5)) < 0).max(Comparator.comparing(DeviceVO::getTime));
            // 出现停车
            if (stopIotInfoDtoOptional.isPresent()) {
                // 则取值时间为 最后一次停车后1分钟时间段，取1/3
                DeviceVO stopIotInfoDto = stopIotInfoDtoOptional.get();
                Instant startInstant = stopIotInfoDto.getTime().plusSeconds(60);
                Duration durationTime = Duration.between(startInstant, endTime.atZone(ZoneId.systemDefault()).toInstant());
                // 取 中间 1/3 时间数据
                long startPoint = durationTime.toMillis() / 3;//开始位置
                long endPoint = durationTime.toMillis() / 3 * 2;//结束位置
                long startEpochMilli = startInstant.toEpochMilli();
                long startMilli = startEpochMilli + startPoint;
                long endMilli = startEpochMilli + endPoint;
                // 如果生产时间<=1分钟,不计算工艺参数 结束
                if ((endMilli - startMilli) <= 1000 * 60) {
                    log.info("[StenterReverseAlgorithmService][reverseAlgorithmForStenter]minutes is {},生产时间大于3分钟，最一次停车后1分钟时间 到 中车结束时间取1/3后，生产时间<=1分钟,不计算工艺参数 结束,startMilli is {},endMilli is {}",minutes,startMilli,endMilli);
                    caleIotInfoDtoList = Lists.newArrayList();
                }
                caleIotInfoDtoList = caleIotInfoDtoList.stream().filter(iotInfoDto -> iotInfoDto.getTime().toEpochMilli() >= startMilli && iotInfoDto.getTime().toEpochMilli() <= endMilli).collect(Collectors.toList());
                if (CollectionUtils.isEmpty(caleIotInfoDtoList)) {
                    log.info("[StenterReverseAlgorithmService][reverseAlgorithmForStenter]minutes is {},生产时间大于3分钟，最一次停车后1分钟时间 到 中车结束时间取1/3后，生产时间>1分钟,但是数据为空 结束,startMilli is {},endMilli is {}",minutes,startMilli,endMilli);
                    caleIotInfoDtoList = Lists.newArrayList();
                }
                log.info("[StenterReverseAlgorithmService][reverseAlgorithmForStenter]minutes is {},生产时间大于3分钟，最一次停车后1分钟时间 到 中车结束时间取1/3后，生产时间>1分钟,数据正常,startMilli is {},endMilli is {}",minutes,startMilli,endMilli);

            } else {
                // 生产时间>=3分钟, 没有停车 ,则取值时间为生产时间中间1/3时间段
                long s = duration.toMillis() / 3;//开始位置
                long e = duration.toMillis() / 3 * 2;//结束位置
                long datum = startTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                long start = datum + s;
                long end = datum + e;
                caleIotInfoDtoList = caleIotInfoDtoList.stream().filter(iotInfoDto -> {
                    long epochMilli = iotInfoDto.getTime().toEpochMilli();
                    return epochMilli >= start && epochMilli <= end;

                }).collect(Collectors.toList());
                if (CollectionUtils.isEmpty(caleIotInfoDtoList)) {
                    log.info("[StenterReverseAlgorithmService][reverseAlgorithmForStenter]minutes is {},生产时间大于3分钟，没有停车,取1/3后，生产时间>1分钟,但是无上报数据",minutes);
                    caleIotInfoDtoList = Lists.newArrayList();
                }
                log.info("[StenterReverseAlgorithmService][reverseAlgorithmForStenter]minutes is {},生产时间大于3分钟，没有停车,数据正常,start is {},end is {}",minutes,start,end);

            }
        }

        if (CollectionUtils.isEmpty(caleIotInfoDtoList)) {
            // 取 最后一次上报数据（非停车）数据需要
            Optional<DeviceVO> iotInfoDtoOptional = allIotInfoDtoList.stream().filter(iotInfoDto -> Objects.nonNull(iotInfoDto.getSpeed()) && iotInfoDto.getSpeed().compareTo(new BigDecimal(5)) >= 0).max(Comparator.comparing(DeviceVO::getTime));
            iotInfoDtoOptional.ifPresent(caleIotInfoDtoList::add);
        }
        List<BigDecimal> brushFeedList = new ArrayList<>();
        for (DeviceVO iotInfoDto : caleIotInfoDtoList) {
            // 毛刷超喂计算逻辑：· 左右毛刷，每组数据取较大值一侧的超喂； 取平均值，精确到0.1%。
            BigDecimal leftBrushFeed = iotInfoDto.getLeftBrushFeed();
            BigDecimal rightBrushFeed = iotInfoDto.getRightBrushFeed();
            Optional<BigDecimal> brushFeed = Lists.newArrayList(leftBrushFeed, rightBrushFeed).stream().filter(Objects::nonNull).max(Comparator.comparing(brush -> brush));
            brushFeed.ifPresent(brushFeedList::add);
        }

        BaseTenterCraft baseTenterCraft = new BaseTenterCraft();

        // 每条数据 优先取设定值，设定值取不到取实际值
        LinkedHashMap<String, BigDecimal> maxSameMap = IotInfoUtils.getEachSectionSettingOrActValue(caleIotInfoDtoList, StenterInfluxDBEnum.geMaxSameKeyAndSettingKey(), 2);
        log.info("[StenterReverseAlgorithmService][reverseAlgorithmForStenter] maxSameMap is {}",maxSameMap);

        // 取相同最多的值，如有多个相同值，取最大值; 四舍五入精确到整数
        LinkedHashMap<String, BigDecimal> dryingRoomActualMap = IotInfoUtils.filterSettingOrActMapValueByKey(maxSameMap, StenterInfluxDBEnum.DRYING_ROOM_ACTUAL_TEMP,3);
        log.info("[StenterReverseAlgorithmService][reverseAlgorithmForStenter] dryingRoomActualMap is {}",dryingRoomActualMap);
        baseTenterCraft.setDryingRoomPresetTemp(transferMapKey(dryingRoomActualMap,StenterInfluxDBEnum.DRYING_ROOM_ACTUAL_TEMP.getActKey(), StenterInfluxDBEnum.DRYING_ROOM_ACTUAL_TEMP.getSettingKey()));
        // 去掉前2节和最后1节，取剩余烘箱计算结果；
        baseTenterCraft.setDryingRoomTemp(BigDecimalUtils.filterMaxSameValueFromList(BigDecimalUtils.filterFirstSecondAndLastValue(dryingRoomActualMap)));

        // 2 门幅计算逻辑
        // 如有「总门幅」值，则取「总门幅」值，如无，则取倒数第3节烘箱门幅值
        LinkedHashMap<String, BigDecimal> totalAmplitudeMap = IotInfoUtils.filterSettingOrActMapValueByKey(maxSameMap, StenterInfluxDBEnum.TOTAL_AMPLITUDE,3);
        BigDecimal amplitude = null;
        if (MapUtils.isNotEmpty(totalAmplitudeMap)) {
            amplitude = totalAmplitudeMap.get(StenterInfluxDBEnum.TOTAL_AMPLITUDE.getActKey());
        }
        LinkedHashMap<String, BigDecimal> doorWidthActualMap = IotInfoUtils.filterSettingOrActMapValueByKey(maxSameMap, StenterInfluxDBEnum.DOOR_WIDTH_ACTUAL,3);
        log.info("[StenterReverseAlgorithmService][reverseAlgorithmForStenter] doorWidthActualMap is {}",doorWidthActualMap);
        if (MapUtils.isNotEmpty(doorWidthActualMap)) {
            baseTenterCraft.setDoorWidthActual(doorWidthActualMap);
            if (Objects.isNull(amplitude)) {
                amplitude = doorWidthActualMap.get(StenterInfluxDBEnum.DOOR_WIDTH_ACTUAL.getActKey() + 8);
            }
        } else {
            // 前后门幅
            LinkedHashMap<String, BigDecimal> frontDoorWidthActualMap = IotInfoUtils.filterSettingOrActMapValueByKey(maxSameMap, StenterInfluxDBEnum.FRONT_DOOR_WIDTH_ACTUAL,3);
            LinkedHashMap<String, BigDecimal> rearDoorWidthActualMap = IotInfoUtils.filterSettingOrActMapValueByKey(maxSameMap, StenterInfluxDBEnum.REAR_DOOR_WIDTH_ACTUAL,3);

            BigDecimal frontDoorWidthActual = frontDoorWidthActualMap.get((StenterInfluxDBEnum.FRONT_DOOR_WIDTH_ACTUAL.getActKey()));
            BigDecimal rearDoorWidthActual = rearDoorWidthActualMap.get((StenterInfluxDBEnum.REAR_DOOR_WIDTH_ACTUAL.getActKey()));

            LinkedHashMap<String, BigDecimal> baseDoorWidthActualMap = BigDecimalUtils.buildJsonByFirstAndLastValue(StenterInfluxDBEnum.DOOR_WIDTH_ACTUAL.getActKey(), 10, frontDoorWidthActual, rearDoorWidthActual, amplitude);
            baseTenterCraft.setDoorWidthActual(baseDoorWidthActualMap);

        }
        if (Objects.nonNull(amplitude)) {
            baseTenterCraft.setTotalAmplitude(String.valueOf(amplitude));
        }

        // 循环风计算逻辑：去掉前边2节和最后1节烘箱的循环风数据； 取相同最多的值
        //· 如循环风数据 ≤ 100，则为按百分比计算模式，取相同最多的值，精确到1%；如果有小数，4舍5入保留整数
        //· 如循环风数据 > 100，则为按转速计算模式，取相同最多的值，精确到100；0< A < 50，A取0；50<= A < 100，A取100。
        LinkedHashMap<String, BigDecimal> cycleWindSpeedMap = IotInfoUtils.filterSettingOrActMapValueByKey(maxSameMap, StenterInfluxDBEnum.CYCLE_WIND_SPEED,2);
        log.info("[StenterReverseAlgorithmService][reverseAlgorithmForStenter] cycleWindSpeedMap is {}",cycleWindSpeedMap);
        baseTenterCraft.setSpeciCycleWindSpeed(transferMapKey(cycleWindSpeedMap,StenterInfluxDBEnum.CYCLE_WIND_SPEED.getActKey(),StenterInfluxDBEnum.CYCLE_WIND_SPEED.getSettingKey()));
        baseTenterCraft.setFanSpeed(BigDecimalUtils.filterMaxSameValueFromList(BigDecimalUtils.filterFirstSecondAndLastValue(cycleWindSpeedMap)));

        // 排风计算逻辑：取相同最多的值
        LinkedHashMap<String, BigDecimal> windSpeedMap = IotInfoUtils.filterSettingOrActMapValueByKey(maxSameMap, StenterInfluxDBEnum.WIND_SPEED,2);
        log.info("[StenterReverseAlgorithmService][reverseAlgorithmForStenter] windSpeedMap is {}",windSpeedMap);
        baseTenterCraft.setSpeciWindSpeed(transferMapKey(windSpeedMap,StenterInfluxDBEnum.WIND_SPEED.getActKey(),StenterInfluxDBEnum.WIND_SPEED.getSettingKey()));
        if (MapUtils.isNotEmpty(windSpeedMap)) {
            List<BigDecimal> windSpeedList = new ArrayList<>(windSpeedMap.values());
            baseTenterCraft.setExhaustSpeed(BigDecimalUtils.filterMaxSameValueFromList(windSpeedList));
        }

        // 车速 连续出现2次(含)以上数值中的最大值，如有效时间段内无连续值，则取最大值；精确到整数
        LinkedHashMap<String, BigDecimal> speedMap = IotInfoUtils.getEachSectionSettingOrActValue(caleIotInfoDtoList, StenterInfluxDBEnum.getConsecutiveDigitsKeyAndSettingKey(), 3);
        log.info("[StenterReverseAlgorithmService][reverseAlgorithmForStenter] speedMap is {}",speedMap);
        baseTenterCraft.setSpeed(speedMap.get(StenterInfluxDBEnum.SPEED.getActKey()));

        // 上超喂/下超喂计算逻辑：取平均值 4舍5入保留1位小数；
        LinkedHashMap<String, BigDecimal> feedMap = IotInfoUtils.getEachSectionSettingOrActValue(caleIotInfoDtoList, StenterInfluxDBEnum.getAvgKeyAndSettingKey(), 1);
        log.info("[StenterReverseAlgorithmService][reverseAlgorithmForStenter] feedMap is {}",feedMap);
        BigDecimal topFeed = feedMap.get(StenterInfluxDBEnum.TOP_FEED.getActKey());
        baseTenterCraft.setTopFeed(topFeed);
        BigDecimal lowerFeed = feedMap.get(StenterInfluxDBEnum.LOWER_FEED.getActKey());
        baseTenterCraft.setLowerFeed(lowerFeed);

        // 毛刷超喂计算逻辑：· 左右毛刷，每组数据取较大值一侧的超喂； 取平均值，精确到0.1%。
        BigDecimal brushFeed = BigDecimalUtils.getAvgByList(brushFeedList);
        baseTenterCraft.setBrushFeed(brushFeed);

        // 数据校验
        IotDataRangeEnum.checkDataOutOfRange(baseTenterCraft);
        return baseTenterCraft;
    }

    /**
     * key为 dryingRoomActualTemp,转为 dryingRoomPresetTemp
     */
    private Map<String,BigDecimal> transferMapKey(Map<String,BigDecimal> map,String currentKey,String replaceKey){
        Map<String,BigDecimal> dryingRoomPresetTempMap = new LinkedHashMap<>();
        if (MapUtils.isEmpty(map)) {
            return dryingRoomPresetTempMap;
        }
        map.forEach((k,v) -> {
            String key = k.replace(currentKey, replaceKey);
            dryingRoomPresetTempMap.put(key,v);
        });
        return dryingRoomPresetTempMap;
    }
}
